package com.region.loadbalancer.http.ok3;

import com.region.common.adapter.logging.LoggerAdapter;
import com.region.common.adapter.logging.LoggerAdapterFactory;
import com.region.common.utils.ReflectUtils;
import com.region.loadbalancer.LoadBalancerManager;
import com.region.loadbalancer.group.Server;
import com.region.plugin.Interceptor;
import com.region.plugin.ProceedPoint;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.internal.connection.RealCall;

import java.lang.reflect.Method;
import java.net.URL;

/**
 * Ok Http 3 Client Interceptor
 *
 * @author liujieyu
 * @date 2023/5/31 19:38
 * @desciption
 */
public class OkHttp3ClientInterceptor implements Interceptor {

    private final LoggerAdapter loggerAdapter = LoggerAdapterFactory.getLogger(getClass());

    private LoadBalancerManager loadBalancerManager;

    public OkHttp3ClientInterceptor(LoadBalancerManager loadBalancerManager) {
        this.loadBalancerManager = loadBalancerManager;
    }

    @Override
    public Object proceed(ProceedPoint proceed) throws Throwable {
        Method method = (Method) ReflectUtils.getFieldValueByObject("method", proceed);
        if ("execute".equals(method.getName())) {
            RealCall realCall = (RealCall) ReflectUtils.getFieldValueByObject("proxy", proceed);
            // Get Request Object
            Request request = (Request) ReflectUtils.getFieldValueByObject("originalRequest", realCall);
            // Get Http Url
            HttpUrl httpUrl = (HttpUrl) ReflectUtils.getFieldValueByObject("url", request);
            // Get group server name
            URL url = new URL(String.valueOf(ReflectUtils.getFieldValueByObject("url", httpUrl)));
            return loadBalancerRequest(realCall, url.getHost(), proceed);
        } else {
            return proceed.proceed();
        }
    }

    /**
     * Load balancer request http
     *
     * @param realCall
     * @param groupName
     * @param proceedPoint
     * @return
     */
    private Object loadBalancerRequest(RealCall realCall, String groupName, ProceedPoint proceedPoint) {
        RealCall cloneRealCall = realCall.clone();
        ReflectUtils.setFieldValue(proceedPoint, "proxy", cloneRealCall);
        Server server = loadBalancerManager.chooseServer(groupName);
        System.out.println(server);
        if (server == null) {
            loggerAdapter.warn("Get load balancing server is null");
            throw new IllegalStateException("Get Load Balancing Service Exception");
        }
        Request request = (Request) ReflectUtils.getFieldValueByObject("originalRequest", cloneRealCall);
        // Get Http Url
        HttpUrl httpUrl = (HttpUrl) ReflectUtils.getFieldValueByObject("url", request);

        ReflectUtils.setFieldValue(httpUrl, "host", server.getHost());
        ReflectUtils.setFieldValue(httpUrl, "port", server.getPort());
        long time = System.nanoTime();
        try {
            Object realVal = proceedPoint.proceed();
            // Save Ok Http request time
            loadBalancerManager.saveServerResponseTime(groupName, server, 1.0 * (System.nanoTime() - time) / (1000 * 1000));
            return realVal;
        } catch (Throwable throwable) {
            loggerAdapter.error(throwable.getMessage(), throwable);
            loadBalancerManager.submitAnomalyServer(groupName, server);
            return this.loadBalancerRequest(realCall, groupName, proceedPoint);
        }
    }
}
